<!--
	前端入口
-->
<!DOCTYPE html>

<html lang="en">
	<!-- 配置脚本 -->
	<head>
		<!-- 标题 css文件 -->
		<title>CNN 网络可视化</title>
		<link rel="stylesheet" href="../css/drawingboard2.css">
		<link rel="stylesheet" href="../css/main.css?v=1.2">
		<link rel="stylesheet" href="../css/katex.min.css">
		
		<!-- 视窗 字符集 -->
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">

		<!-- 引入的外部库 -->
		<script src="../js/import/three.min.js"></script> <!-- WebGL 3D -->
		<script src="../js/import/GeometryUtils.js"></script>
		<script src="../js/import/processing.min.js"></script>
		<script src="../js/import/sylvester.src.js"></script> <!-- 矩阵运算 -->
		<script src="../js/import/math.min.js"></script> 
		<script src="../js/import/jquery-1.11.2.min.js"></script>
		<script src="../js/import/stats.min.js"></script> <!-- 信息窗 -->

		<!-- 画板脚本 -->
		<script src="../js/drawingboard/utils.js"></script>
		<script src="../js/drawingboard/board.js"></script>
		<script src="../js/drawingboard/controls/control.js"></script>
		<script src="../js/drawingboard/controls/color.js"></script>
		<script src="../js/drawingboard/controls/drawingmode.js"></script>
		<script src="../js/drawingboard/controls/navigation.js"></script>
		<script src="../js/drawingboard/controls/size.js"></script>
		<script src="../js/drawingboard/controls/download.js"></script>

		<!-- cnn test 脚本 -->
		<script src="../js/nn/convnet_weights.js"></script>
		<script src="../js/nn/common.js"></script>
		<script src="../js/nn/conv.js"></script>
		<script src="../js/createText.js"></script>

		<script src="../js/droid_sans_regular.typeface.js"></script>
		<script src="../js/colormaps/myColorMap_dark.js"></script>

		<!-- 相机控制 -->
		<script src="../js/camera.js"></script>
		<script src="../js/myOrbitControls.js"></script> 
		<script src="../js/katex.min.js"></script>
	</head>

	<!-- 页面 -->
	<body>
		<!-- 鼠标选择组件框 -->
		<div id="webgl_container">
			<!-- 节点信息框 -->
			<div id="infobox">
				<!-- 位置信息 -->
				<div id="nodeType"></div>

				<!-- 显示框 -->
				<div id="imageInputContainer">
					<div id="inputImage">
						<div class="label" id="imageLabel">输入图像块:</div>
						<div id="inputCanvasContainer">
							<canvas id="inputCanvas"></canvas>
						</div>
					</div>
					<div id="filterImage">
						<div class="label" id="filterLabel">卷积核:</div>
						<div id="filterCanvasContainer">
							<canvas id="filterCanvas"></canvas>
						</div>
					</div>
				</div>

				<!-- 信息框 -->
				<div id="nodeInputContainer">
					<div class="label">该元素权重: </div>
					<div id="nodeInput" class="math"></div>
				</div>
				<div id="calcContainer">
					<div class="label">预测: </div>
					<div id="calc" class="math"></div>
				</div>
				<div id="nodeOutputContainer">
					<div class="label">输出: </div>
					<div id="nodeOutput" class="math"></div>
				</div>
			</div>
		</div>

		<!-- 画板 -->
		<div id="canvasContainer">
			<div id="drawingInterface">
				<div class="instructions">此处写下数字</div>
				<div class="board" id="custom-board"></div>
				<div class="drawingOutput">
					<div class="row">
						<div class="info">输入图像:</div>
						<div class="tinyBoard">
							<canvas id="tiny"></canvas>
						</div>
					</div>
					<div class="row">
						<div class="info cell">最大可能:</div>
						<div id="ans1" class="ans cell"></div>
					</div>
					<div class="row cell">
						<div class="info cell">第二可能:</div>
						<div id="ans2" class="ans cell"></div>
					</div>
				</div>
			</div>
		</div>

		<!-- 链接 -->
		<div id="linkback">by 
			<a href="https://github.com/yaBorn">YangBowen</a>
			、<a href="https://github.com/hd233yui">HuangDonghao</a>
			<br>
			code:
			<a href="https://gitee.com/yaborn/Visualization_CNN">gitee</a>
			/ <a href="https://github.com/yaBorn/Visualization_CNN">github</a>
		</div>
	</body>

	<!-- 运行脚本 -->
	<script>
		// 控制 内存参数
		var container, stats;
		var camera, scene, renderer;
		var pickingData = [], pickingTexture, pickingScene;
		var objects = [];
		var highlightBox; // 光标选中
		var allZeroes = true; // 未绘制数字

		// 网络结构 矩阵信息 
		var nPixels = 32 * 32;
		var nConvNodes_1 = 28 * 28 * 6;
		var nConvNodes_1_down = 14 * 14 * 6;
		var nConvNodes_2 = 10 * 10 * 16;
		var nConvNodes_2_down = 5 * 5 * 16;
		var filterSize_1 = 5;
		var filterSize_2 = 5;
		var nConvFilters_1 = 6;
		var nConvFilters_2 = 16;
		var nConvLayers = 2;
		var nHiddenNodes_1 = 120;
		var nHiddenNodes_2 = 100;
		var nHiddenLayers = 2;
		var nFinalNodes = 10;
		var nNodes = nPixels 
			+nConvNodes_1 +nConvNodes_1_down 
			+nConvNodes_2 +nConvNodes_2_down 
			+nHiddenNodes_1 +nHiddenNodes_2 
			+nFinalNodes;
		console.log('nNodes = ' + nNodes);

		// 根据网络结构 加载数据 供cnn计算使用
		var allNodeNums = new Array(nNodes);
		var allNodeInputs = new Array(nNodes);
		var allNodeOutputs = new Array(nNodes);
		var allNodeOutputsRaw = new Array(nNodes);
		var finalOutputID = 0;
		var isComputed = false;
		var goodStart = false;
		var hidden_weights_1a, hidden_weights_2a, final_weightsa;
		var conv_weights_1a;
		var conv_weights_2a;

		// 卷积核
		var filterNum, filterNum_below;
		var nodeType;
		var keeperIndices;
		var nKeepers;

		// 选中方块
		var interID, row, col, ind_below;
		var intersected = false; // 是否选择某方块

		// 射线检测
		var posX = [], posY = [], posZ = [], layerNum = [];

		// 屏幕与光标参数
		var originalWidth = window.innerWidth;
		var originalHeight = window.innerHeight;
		var mouse = new THREE.Vector2();
		var mousepx = new THREE.Vector2();

		// 加载组件
		var inputCanvasContainer = document.getElementById("inputCanvasContainer");
		var filterCanvasContainer = document.getElementById("filterCanvasContainer");
		var inputCanvas = document.getElementById("inputCanvas");
		var filterCanvas = document.getElementById("filterCanvas");
		var inputCtx = inputCanvas.getContext("2d");
		var filterCtx = filterCanvas.getContext("2d");

		// 数字结果模型
		var nums = "0 1 2 3 4 5 6 7 8 9",
			height = 1,
			size = 9,
			hover = 260,
			curveSegments = 8,
			bevelThickness = 2,
			bevelSize = 1.5,
			bevelSegments = 3,
			bevelEnabled = false,
			font = "droid sans", // 字体
			weight = "normal", // 标准粗体
			style = "normal"; // 标准正斜体

		// 画板
		var customBoard = new DrawingBoard.Board('custom-board', {
			background: "#000",
			color: "#fff",
			size: 30,
			controls: [
				{ Navigation: { back: false, forward: false } },
				{ DrawingMode: { filler: false } }
			],
			controlsPosition: "bottom right",
			webStorage: 'session',
			droppable: false
		});
		var tinyCtx = $("#tiny")[0].getContext("2d");
		tinyCtx.scale(0.1, 0.1);

		init();
		loadData();
		animate();

		// 网络初始化
		function init() {
			container = document.getElementById("webgl_container");
			camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 5000);
			camera.position.z = 1000;
			camera.position.y = 200;

			scene = new THREE.Scene();

			pickingScene = new THREE.Scene();
			pickingTexture = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight);
			pickingTexture.minFilter = THREE.LinearFilter;
			pickingTexture.generateMipmaps = false;

			// 光源设置
			var light = new THREE.SpotLight(0xffffff, 1.0);
			light.name = "light";
			light.position.set(0, 500, 2000);
			scene.add(light);

			var light2 = new THREE.SpotLight(0xffffff, 0.5);
			light2.name = "light";
			light2.position.set(0, 200, -1000);
			scene.add(light2);

			var light3 = new THREE.AmbientLight(0xffffff);
			light3.name = "light";
			scene.add(light3);

			// 光标选择高亮块
			highlightBox = new THREE.Mesh(
				new THREE.BoxGeometry(12, 12, 12),
				new THREE.MeshLambertMaterial({ color: 0xffff00 }
				));
			highlightBox.visible = false;
			scene.add(highlightBox);

			hideInputBox = new THREE.Mesh(
				new THREE.BoxGeometry(340, 20, 340),
				new THREE.MeshLambertMaterial({ color: 0x00ff00, transparent: true, opacity: 0.1 }
				));
			hideInputBox.position.set(0, -250, 0);
			hideInputBox.visible = true;

			// webGL绘制
			renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
			renderer.setClearColor(0x000000, 0);
			renderer.setPixelRatio(window.devicePixelRatio);
			renderer.setSize(window.innerWidth, window.innerHeight);
			renderer.sortObjects = false;
			container.appendChild(renderer.domElement);

			// 相机控制
			var cameraControls;
			cameraControls = new THREE.OrbitControls(camera, renderer.domElement);
			cameraControls.target.set(0, 0, 0);

			stats = new Stats();
			stats.domElement.style.position = 'absolute';
			stats.domElement.style.top = '0px';
			container.appendChild(stats.domElement);

			// 左键旋转角度 右键平移 滚轮/中建放大缩小
			renderer.domElement.addEventListener('mousemove', onMouseMove);
			renderer.domElement.addEventListener('mousedown', onMouseDown);
			renderer.domElement.addEventListener('click', onClick);
			renderer.domElement.addEventListener('mouseup', onMouseUp);
			window.addEventListener('resize', onWindowResize, false); // 窗口大小变化 回调函数

			// 计算机 CNN 网络输出
			getNNOutput();
		}
		// 窗口大小变化 回调函数
		function onWindowResize(e) {
			camera.aspect = window.innerWidth / window.innerHeight;
			camera.updateProjectionMatrix(); // 更新视角
			renderer.setSize(window.innerWidth, window.innerHeight);
			render();
		}

		// 加载数据 更新 CNN
		function loadData() {
			var nodeCount = 0;
			// CNN 开始卷积
			$.getJSON('./../js/nn/webgl_convnet2.json', function (data) {
				$.each(data.nodes, function (i, node) {
					posX[nodeCount] = node.x;
					posY[nodeCount] = node.y;
					posZ[nodeCount] = node.z;
					layerNum[nodeCount] = node.layerNum;
					nodeCount++;
				});
			})
			.error(function () {
				console.log('error');
			})
			.done(function () {
				// 根据每层的矩阵数据 绘制节点块
				setupWeightArrays(); // 更新权重
				createText(nums, 0, 100);
				drawCubes(); // 绘制块
				drawEdges();
			});
		}
		
		// 网络模型渲染
		function animate() {
			requestAnimationFrame(animate);
			
			// 光标选中某节点
			pick(); // 选中节点
			updateEdges(); // 绘制上一层连接到该节点的射线

			// 点击某个box 显示节点信息框
			if (!highlightBox.visible) {
				document.getElementById("infobox").style.visibility = "hidden";
			}
			renderer.render(scene, camera);

			// 更新界面
			stats.update();
		}
		// 鼠标置于节点 绘制射线
		function updateEdges() {
			var r = 1, g = 1, b = 1, rw = 1, gw, bw, i, j, v, colorNum, weight;
			var vertAdjust = 3;
			var numChildren = scene.children.length;
			var colors = [];
			vertCount = 0;
			// 遍历场景对象到网络窗口
			for (var c = 0; c < numChildren; c++) {
				if (scene.children[c].name == 'edges') {
					var object = scene.children[c];
					object.geometry.dispose();

					var lineGeom = new THREE.Geometry();
					lineGeom.dynamic = true;
					
					// 如果光标选择节点
					if (highlightBox.visible) {
						ind = interID;
						// 节点属于卷积层 1
						if (layerNum[interID] == 1) {
							// 遍历前一层连接节点
							// 强度与卷积核同值
							for (j = 1; j <= nPixels; j++) {
								ind_below = j - 1;
								temp_below = allNodeNums[ind_below] - 1;
								var row_below = temp_below % 32;
								var col_below = math.floor(temp_below / 32);

								if (row_below > row - 1 && row_below < row + 5 && col_below > col - 1 && col_below < col + 5) {
									var ro = row_below - row;
									var co = col_below - col;
									var weight = conv_nodes[0][filterNum].e(ro + 1, co + 1);

									lineGeom.vertices.push(new THREE.Vector3(posX[ind_below], posY[ind_below] + vertAdjust, posZ[ind_below]));
									lineGeom.vertices.push(new THREE.Vector3(posX[ind], posY[ind] - 3, posZ[ind]));

									if (isComputed) {
										v = allNodeOutputs[ind_below];
										colorNum = math.round(v * 99);
										r = redLookup[colorNum];
										g = greenLookup[colorNum];
										b = blueLookup[colorNum];
									} else {
										r = 0.7;
										g = 0.7;
										b = 0.7;
									}
									v = conv_weights_1a[filterNum][ro][co];
									colorNum = math.round(v * 99);
									rw = 0;
									gw = greenLookup[colorNum];
									bw = blueLookup[colorNum];

									colors[vertCount] = new THREE.Color(rw, gw, bw);
									vertCount++;
									colors[vertCount] = new THREE.Color(rw, gw, bw);
									vertCount++;
								}
							}
						} 
						// 属于池化层1
						else if (layerNum[interID] == 2) {
							for (j = 1; j <= nConvNodes_1; j++) {
								ind_below = nPixels + j - 1;
								temp_below = allNodeNums[ind_below] - 1;
								filterNum_below = math.floor((ind_below - nPixels) / (28 * 28));
								var row_below = temp_below % 28;
								var col_below = math.floor(temp_below / 28);
								if (filterNum_below == filterNum) {
									if (math.floor(row_below / 2) == row && math.floor(col_below / 2) == col) {
										var ro = row_below - row;
										var co = col_below - col;

										lineGeom.vertices.push(new THREE.Vector3(posX[ind_below], posY[ind_below] + vertAdjust, posZ[ind_below]));
										lineGeom.vertices.push(new THREE.Vector3(posX[ind], posY[ind] - 3, posZ[ind]));

										if (isComputed) {
											v = allNodeOutputs[ind_below];
											colorNum = math.round(v * 99);
											r = redLookup[colorNum];
											g = greenLookup[colorNum];
											b = blueLookup[colorNum];
										} else {
											r = 0.7;
											g = 0.7;
											b = 0.7;
										}
										v = 1;
										colorNum = math.round(v * 99);
										rw = 0;
										gw = greenLookup[colorNum];
										bw = blueLookup[colorNum];

										colors[vertCount] = new THREE.Color(rw, gw, bw);
										vertCount++;
										colors[vertCount] = new THREE.Color(rw, gw, bw);
										vertCount++;
									}
								}
							}
						} 
						// 节点属于卷积层 2
						else if (layerNum[interID] == 3) {
							for (j = 1; j <= nConvNodes_1_down; j++) {
								ind_below = nPixels + nConvNodes_1 + j - 1;
								temp_below = allNodeNums[ind_below] - 1;
								filterNum_below = math.floor((ind_below - nPixels - nConvNodes_1) / (14 * 14));
								var row_below = temp_below % 14;
								var col_below = math.floor(temp_below / 14);
								if (keepers.e(filterNum_below + 1, filterNum + 1) == 1) {
									if (row_below > row - 1 && row_below < row + 5 && col_below > col - 1 && col_below < col + 5) {
										var ro = row_below - row;
										var co = col_below - col;
										var filterKeeper = keeperIndices[filterNum][filterNum_below];
										var weight = conv_nodes[1][filterNum][filterKeeper].e(ro + 1, co + 1);

										lineGeom.vertices.push(new THREE.Vector3(posX[ind_below], posY[ind_below] + vertAdjust, posZ[ind_below]));
										lineGeom.vertices.push(new THREE.Vector3(posX[ind], posY[ind] - 3, posZ[ind]));

										if (isComputed) {
											v = allNodeOutputs[ind_below];
											colorNum = math.round(v * 99);
											r = redLookup[colorNum];
											g = greenLookup[colorNum];
											b = blueLookup[colorNum];
										} else {
											r = 0.7;
											g = 0.7;
											b = 0.7;
										}
										v = conv_weights_2a[filterNum][filterNum_below][ro][co];
										colorNum = math.round(v * 99);
										rw = 0;
										gw = greenLookup[colorNum];
										bw = blueLookup[colorNum];

										colors[vertCount] = new THREE.Color(rw, gw, bw);
										vertCount++;
										colors[vertCount] = new THREE.Color(rw, gw, bw);
										vertCount++;
									}
								}
							}
						} 
						// 节点属于池话层 2
						else if (layerNum[interID] == 4) {
							for (j = 1; j <= nConvNodes_1; j++) {
								ind_below = nPixels + nConvNodes_1 + nConvNodes_1_down + j - 1;
								temp_below = allNodeNums[ind_below] - 1;
								filterNum_below = math.floor((ind_below - nPixels - nConvNodes_1 - nConvNodes_1_down) / (10 * 10));
								var row_below = temp_below % 10;
								var col_below = math.floor(temp_below / 10);
								if (filterNum_below == filterNum) {
									if (math.floor(row_below / 2) == row && math.floor(col_below / 2) == col) {
										var ro = row_below - row;
										var co = col_below - col;

										lineGeom.vertices.push(new THREE.Vector3(posX[ind_below], posY[ind_below] + vertAdjust, posZ[ind_below]));
										lineGeom.vertices.push(new THREE.Vector3(posX[ind], posY[ind] - 3, posZ[ind]));

										if (isComputed) {
											v = allNodeOutputs[ind_below];
											colorNum = math.round(v * 99);
											r = redLookup[colorNum];
											g = greenLookup[colorNum];
											b = blueLookup[colorNum];
										} else {
											r = 0.7;
											g = 0.7;
											b = 0.7;
										}
										v = 1;
										colorNum = math.round(v * 99);
										rw = 0;
										gw = greenLookup[colorNum];
										bw = blueLookup[colorNum];

										colors[vertCount] = new THREE.Color(rw, gw, bw);
										vertCount++;
										colors[vertCount] = new THREE.Color(rw, gw, bw);
										vertCount++;
									}
								}
							}
						} 
						// 节点属于隐含层1
						else if (layerNum[interID] == 5) {
							for (j = 1; j <= nConvNodes_2_down; j++) {

								ind_below = nPixels + nConvNodes_1 + nConvNodes_1_down + nConvNodes_2 + j - 1;
								var weight = hidden_weights_1.e(allNodeNums[ind], allNodeNums[ind_below]);

								lineGeom.vertices.push(new THREE.Vector3(posX[ind_below], posY[ind_below] + vertAdjust, posZ[ind_below]));
								lineGeom.vertices.push(new THREE.Vector3(posX[ind], posY[ind] - 3, posZ[ind]));

								if (isComputed) {
									v = allNodeOutputs[ind_below];
									colorNum = math.round(v * 99);
									r = redLookup[colorNum];
									g = greenLookup[colorNum];
									b = blueLookup[colorNum];
								} else {
									r = 0.7;
									g = 0.7;
									b = 0.7;
								}
								v = hidden_weights_1a[allNodeNums[ind] - 1][allNodeNums[ind_below] - 1];
								colorNum = math.round(v * 99);
								rw = 0;
								gw = greenLookup[colorNum];
								bw = blueLookup[colorNum];

								colors[vertCount] = new THREE.Color(rw, gw, bw);
								vertCount++;
								colors[vertCount] = new THREE.Color(rw, gw, bw);
								vertCount++;
							}
						} 
						// 节点属于隐含层2
						else if (layerNum[interID] == 6) {
							for (j = 1; j <= nHiddenNodes_1; j++) {
								ind_below = nPixels + nConvNodes_1 + nConvNodes_1_down + nConvNodes_2 + nConvNodes_2_down + j - 1;
								var weight = hidden_weights_2.e(allNodeNums[ind], allNodeNums[ind_below]);

								lineGeom.vertices.push(new THREE.Vector3(posX[ind_below], posY[ind_below] + vertAdjust, posZ[ind_below]));
								lineGeom.vertices.push(new THREE.Vector3(posX[ind], posY[ind] - 3, posZ[ind]));
								if (isComputed) {
									v = allNodeOutputs[ind_below];
									colorNum = math.round(v * 99);
									r = redLookup[colorNum];
									g = greenLookup[colorNum];
									b = blueLookup[colorNum];
								} else {
									r = 0.7;
									g = 0.7;
									b = 0.7;
								}
								v = hidden_weights_2a[allNodeNums[ind] - 1][allNodeNums[ind_below] - 1];
								colorNum = math.round(v * 99);
								rw = 0;
								gw = greenLookup[colorNum];
								bw = blueLookup[colorNum];

								colors[vertCount] = new THREE.Color(rw, gw, bw);
								vertCount++;
								colors[vertCount] = new THREE.Color(rw, gw, bw);
								vertCount++;
							}
						} 
						// 节点属于输出层
						else if (layerNum[interID] == 7) {
							for (j = 1; j <= nHiddenNodes_2; j++) {
								ind_below = nPixels + nConvNodes_1 + nConvNodes_1_down + nConvNodes_2 + nConvNodes_2_down + nHiddenNodes_1 + j - 1;
								var weight = final_weights.e(allNodeNums[ind], allNodeNums[ind_below]);

								lineGeom.vertices.push(new THREE.Vector3(posX[ind_below], posY[ind_below] + vertAdjust, posZ[ind_below]));
								lineGeom.vertices.push(new THREE.Vector3(posX[ind], posY[ind] - 3, posZ[ind]));
								if (isComputed) {
									v = allNodeOutputs[ind_below];
									colorNum = math.round(v * 99);
									r = redLookup[colorNum];
									g = greenLookup[colorNum];
									b = blueLookup[colorNum];
								} else {
									r = 0.7;
									g = 0.7;
									b = 0.7;
								}
								v = final_weightsa[allNodeNums[ind] - 1][allNodeNums[ind_below] - 1];
								colorNum = math.round(v * 99);
								rw = 0;
								gw = greenLookup[colorNum];
								bw = blueLookup[colorNum];

								colors[vertCount] = new THREE.Color(rw, gw, bw);
								vertCount++;
								colors[vertCount] = new THREE.Color(rw, gw, bw);
								vertCount++;
							}
						}
						v = allNodeOutputs[ind];
						colorNum = math.round(v * 99);
						r = redLookup[colorNum];
						g = greenLookup[colorNum];
						b = blueLookup[colorNum];

					}
					var lineMat = new THREE.LineBasicMaterial({ color: 0xffffff, opacity: 1, linewidth: 1, vertexColors: THREE.VertexColors });
					object.material = lineMat;
					object.geometry.colors = colors;
					object.geometry.vertices = lineGeom.vertices;
					object.material.needsUpdate = true;
					object.geometry.colorsNeedUpdate = true;
					object.geometry.verticesNeedUpdate = true;
					break;
				}
			}
		}
		// 鼠标选中节点 显示节点信息
		function pick() {
			// 平面显示 选择节点信息框
			renderer.render(pickingScene, camera, pickingTexture);
			// 读取单个像素的内内存
			var pixelBuffer = new Uint8Array(4);
			// 读取光标选中的节点纹理数据
			renderer.readRenderTargetPixels(pickingTexture, mouse.x, pickingTexture.height - mouse.y, 1, 1, pixelBuffer);
			// 置为id
			var id = (pixelBuffer[0] << 16) | (pixelBuffer[1] << 8) | (pixelBuffer[2]);
			var data = pickingData[id];

			// 点击节点对象
			if (data && pixelBuffer[3]) {
				var temp = pickingData[id].id;
				// 获取该节点位于网络层id
				interID = pickingData[id].id;

				// 根据节点位置 绘制节点信息
				if (layerNum[interID] == 0) {
					nodeType = "输入层";
					filterNum = -1;
					tempInd = allNodeNums[interID] - 1;
					row = (tempInd) % 32;
					col = math.floor(tempInd / 32);
				} 
				else if (layerNum[interID] == 1) {
					nodeType = "卷积层 1";
					filterNum = math.floor((interID - nPixels) / (28 * 28));
					tempInd = allNodeNums[interID] - 1;
					row = (tempInd) % 28;
					col = math.floor(tempInd / 28);
				} 
				else if (layerNum[interID] == 2) {
					nodeType = "池化层 1";
					filterNum = math.floor((interID - nPixels - nConvNodes_1) / (14 * 14));
					tempInd = allNodeNums[interID] - 1;
					row = (tempInd) % 14;
					col = math.floor(tempInd / 14);
				} 
				else if (layerNum[interID] == 3) {
					nodeType = "卷积层 2";
					filterNum = math.floor((interID - nPixels - nConvNodes_1 - nConvNodes_1_down) / (10 * 10));
					tempInd = allNodeNums[interID] - 1;
					row = (tempInd) % 10;
					col = math.floor(tempInd / 10);
				} 
				else if (layerNum[interID] == 4) {
					nodeType = "池化层 2";
					filterNum = math.floor((interID - nPixels - nConvNodes_1 - nConvNodes_1_down - nConvNodes_2) / (5 * 5));
					tempInd = allNodeNums[interID] - 1;
					row = (tempInd) % 5;
					col = math.floor(tempInd / 5);
				} 
				else if (layerNum[interID] == 5) {
					nodeType = "全连接层 1";
					filterNum = -1
				} 
				else if (layerNum[interID] == 6) {
					nodeType = "全连接层 2";
					filterNum = -1
				} 
				else if (layerNum[interID] == 7) {
					nodeType = "输出层";
					filterNum = -1
				}

				// 移动高亮块到鼠标选择的对象 实现选中效果
				if (data.position && data.rotation && data.scale) {
					highlightBox.position.copy(data.position);
					highlightBox.rotation.copy(data.rotation);
					highlightBox.visible = true;
					intersected = true;
					updateInfoBoxPos();
				}
			}
			// 未点击 则高亮块不显示
			else {
				highlightBox.visible = false;
				intersected = false;
			}

			// 设置显示的高亮块
			if (highlightBox.visible == true) {
				var r, g, b;
				if (isComputed) {
					// 高亮块显示参数与 光标选中对象一致
					var v = allNodeOutputs[interID];
					var colorNum = math.round(v * 99);
					r = redLookup[colorNum];
					g = greenLookup[colorNum];
					b = blueLookup[colorNum];
				} 
				else {
					r = 0, g = 0, b = 0;
				}
				highlightBox.material.color.setRGB(r, g, b);
				highlightBox.material.needsUpdate = true;
				
				// 更新 节点信息块窗口
				updateInfoBox();
			}
		}
		// 更新 节点信息块窗口
		function updateInfoBox() {
			var infobox = document.getElementById("infobox");
			var v, colorNum, r, g, b;
			var str;

			if (layerNum[interID] >= 1 && layerNum[interID] <= 4)
				str = nodeType + ", filter " + (filterNum + 1) + ", unit " + allNodeNums[interID]
			else
				str = nodeType + ", unit " + allNodeNums[interID]
			$("#nodeType").html(str);

			if (!allZeroes) {
				str = allNodeInputs[interID].toFixed(2);
			} 
			else {
				str = "0";
			}
			document.getElementById("nodeInput").textContent = str;

			if (!allZeroes) {
				str = allNodeOutputsRaw[interID].toFixed(2);
				if (nodeType == "Output layer" && interID == finalOutputID) {
					str = str + " (max!)"
				}
			} 
			else {
				str = "0";
			}
			document.getElementById("nodeOutput").textContent = str;
			v = allNodeOutputs[interID];
			colorNum = math.round(v * 99);
			r = 0;
			g = math.round(greenLookup[colorNum] * 255);
			b = math.round(blueLookup[colorNum] * 255);
			var bkgStr = "rgb(" + r + "," + g + "," + b + ")";
			console.log(bkgStr);
			document.getElementById("nodeOutput").style.color = bkgStr;

			if (nodeType != "Input layer") {
				if (!allZeroes) {
					if (nodeType == "Downsampling layer 1" || nodeType == "Downsampling layer 2") {
						str = "max pooling";
					} else {
						str = "1.7159 tanh (2/3 * " + allNodeInputs[interID].toFixed(2) + ") = " + allNodeOutputsRaw[interID].toFixed(2);
					}
				} else {
					str = "1.7159 tanh (2/3 * 0) = 0";
				}
			} 
			else {
				if (!allZeroes) {
					str = "(" + allNodeInputs[interID].toFixed(2) + "/255) * 1.275 - 0.1 = " + allNodeOutputsRaw[interID].toFixed(2);
				} else {
					str = "none";
				}
			}
			document.getElementById("calc").textContent = str;
			drawFilter();
		}
	</script>
</html>